package verifycode

import (
	"bytes"
	"fmt"
	"gxcvu-ai/pkg/app"
	"gxcvu-ai/pkg/config"
	emailPkg "gxcvu-ai/pkg/email"
	"gxcvu-ai/pkg/helper"
	"gxcvu-ai/pkg/logger"
	"gxcvu-ai/pkg/redis"
	"html/template"
	"strings"
	"sync"

	"github.com/gin-gonic/gin"
)

type Store interface {
	Set(id string, value string) bool
	Get(id string, clear bool) string
	Verify(id string, answer string, clear bool) bool
}

type VerifyCode struct {
	Store Store
}

var once sync.Once
var internalVerifyCode *VerifyCode

// NewVerifyCode 单例模式获取
func NewVerifyCode() *VerifyCode {
	once.Do(func() {
		internalVerifyCode = &VerifyCode{
			Store: &RedisStore{
				RedisClient: redis.Redis,
				// 增加前缀保持数据库整洁，出问题调试时也方便
				KeyPrefix: config.GetString("app.name") + ":verifycode:",
			},
		}
	})

	return internalVerifyCode
}

func (vc *VerifyCode) SendEmail(email string) error {
	code := vc.generateVerifycode(email)

	// 本地测试
	if app.IsLocal() && strings.HasSuffix(email, config.GetString("verifycode.debug_email_suffix")) {
		return nil
	}

	tmp, err := template.ParseFiles("template/signin_email_template.html")
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	data := gin.H{
		"code":        code,
		"expire_time": config.GetString("verifycode.expire_time"),
		"site_name":   config.GetString("app.name"),
		"site_url":    config.GetString("app.url"),
	}

	execTmp := bytes.Buffer{}
	tmp.Execute(&execTmp, data)

	content := execTmp.String()
	fmt.Println(content)

	emailPkg.NewMailer().Send(emailPkg.Email{
		From: emailPkg.From{
			Address: config.GetString("email.from.address"),
			Name:    config.GetString("email.from.name"),
		},
		To:      []string{email},
		Subject: "Email 验证码",
		Html:    []byte(content),
	})

	return nil
}

// CheckAnswer 检查用户提交的验证码是否正确，key 可以是手机号或者 Email
func (vc *VerifyCode) CheckAnswer(key string, answer string) bool {

	logger.DebugJSON("验证码", "检查验证码", map[string]string{key: answer})

	// 方便开发，在非生产环境下，具备特殊前缀的手机号和 Email后缀，会直接验证成功
	if !app.IsProduction() &&
		(strings.HasSuffix(key, config.GetString("verifycode.debug_email_suffix")) ||
			strings.HasPrefix(key, config.GetString("verifycode.debug_phone_prefix"))) {
		return true
	}

	return vc.Store.Verify(key, answer, false)
}

func (vc *VerifyCode) generateVerifycode(key string) string {
	code := helper.RandomNumber(config.GetInt("verifycode.code_length"))

	if app.IsLocal() {
		code = config.GetString("verifycode.debug_code")
	}

	logger.DebugJSON("验证码", "生成验证码", map[string]string{key: code})

	vc.Store.Set(key, code)
	return code
}
